package dao

import (
	"context"
	"sale-admin/config/mysql"
	"sale-admin/internal/app/grpc/client"
	"sale-admin/internal/app/grpc/protoc/devicec"
	"sale-admin/internal/app/grpc/protoc/system_admin"
	"sale-admin/internal/dal"
	"sale-admin/internal/models"
	"strconv"
	"strings"
	"time"

	sharding "192.168.1.75/go-pkg/db-sharding"
	"192.168.1.75/go-pkg/helper"
	"192.168.1.75/go-pkg/logx"
	"github.com/golang-module/carbon/v2"
	"github.com/samber/lo"
	"gorm.io/gorm"
)

type StareAgeGender struct{}

func getAgeRange(age int64) (ageRange int64) {
	switch {
	case age <= 11:
		ageRange = 1
	case age <= 19:
		ageRange = 2
	case age <= 29:
		ageRange = 3
	case age <= 39:
		ageRange = 4
	case age <= 49:
		ageRange = 5
	default:
		ageRange = 6
	}
	return
}
func (StareAgeGender) Save(ctx context.Context, query *dal.Query, merchantID, robotName string, startTime,
	endTime, stareType, locationID int64, AgeGender []struct {
	Age    int64 `json:"Age"`
	Male   int64 `json:"Male"`
	Female int64 `json:"Female"`
}) (err error) {
	m := query.StareAgeGender
	dbs := sharding.GetDB(ctx, mysql.NewDB(), m.TableName(), startTime, startTime)
	writeDBTx := dal.Use(dbs.WriteDB).StareAgeGender.WithContext(ctx)
	updateDBTx := dal.Use(dbs.UpdateDB).StareAgeGender.WithContext(ctx)

	updateDBTx.Where(
		m.RobotName.Eq(robotName),
		m.StartTime.Eq(startTime),
		m.EndTime.Eq(endTime),
		m.Type.Eq(stareType),
	).Delete()
	for _, v := range AgeGender {
		var data models.StareAgeGender
		data.ID, _ = helper.UUID{}.String()
		data.MerchantID = merchantID
		data.RobotName = robotName
		data.StartTime = startTime
		data.EndTime = endTime
		data.Type = stareType
		data.Age = v.Age
		data.AgeRange = getAgeRange(v.Age)
		data.Male = v.Male
		data.Female = v.Female
		data.LocationID = strconv.FormatInt(locationID, 10)
		data.CreatedAt = time.Now().Unix()
		data.Date = carbon.CreateFromTimestamp(startTime).ToDateString()
		writeDBTx.Create(&data)
	}
	return
}

type StareAgeGenderSummary struct {
	Total           int64 `json:"total"`
	MaleAgeRange1   int64 `json:"male_age_range_1"`
	MaleAgeRange2   int64 `json:"male_age_range_2"`
	MaleAgeRange3   int64 `json:"male_age_range_3"`
	MaleAgeRange4   int64 `json:"male_age_range_4"`
	MaleAgeRange5   int64 `json:"male_age_range_5"`
	MaleAgeRange6   int64 `json:"male_age_range_6"`
	FemaleAgeRange1 int64 `json:"female_age_range_1"`
	FemaleAgeRange2 int64 `json:"female_age_range_2"`
	FemaleAgeRange3 int64 `json:"female_age_range_3"`
	FemaleAgeRange4 int64 `json:"female_age_range_4"`
	FemaleAgeRange5 int64 `json:"female_age_range_5"`
	FemaleAgeRange6 int64 `json:"female_age_range_6"`
}

type StareAgeGenderList struct {
	Date            string `json:"date"`
	RobotName       string `json:"robot_name"`
	MerchantID      string `json:"-"`
	MerchantName    string `json:"merchant_name"`
	ProvinceID      string `json:"-"`
	CityID          string `json:"-"`
	DistrictID      string `json:"-"`
	LocationID      string `json:"-"`
	Province        string `json:"province"`
	City            string `json:"city"`
	District        string `json:"district"`
	Location        string `json:"location"`
	Total           int64  `json:"total"`
	MaleAgeRange1   int64  `json:"male_age_range_1"`
	MaleAgeRange2   int64  `json:"male_age_range_2"`
	MaleAgeRange3   int64  `json:"male_age_range_3"`
	MaleAgeRange4   int64  `json:"male_age_range_4"`
	MaleAgeRange5   int64  `json:"male_age_range_5"`
	MaleAgeRange6   int64  `json:"male_age_range_6"`
	FemaleAgeRange1 int64  `json:"female_age_range_1"`
	FemaleAgeRange2 int64  `json:"female_age_range_2"`
	FemaleAgeRange3 int64  `json:"female_age_range_3"`
	FemaleAgeRange4 int64  `json:"female_age_range_4"`
	FemaleAgeRange5 int64  `json:"female_age_range_5"`
	FemaleAgeRange6 int64  `json:"female_age_range_6"`
}

func (StareAgeGender) StareAnalysis(ctx context.Context, query *dal.Query, merchantID, robotName string,
	startTime, endTime int64, page, limit int) (
	res []StareAgeGenderList, total int64, summary StareAgeGenderSummary, err error,
) {
	m := query.StareAgeGender
	dbs := sharding.GetDB(ctx, mysql.NewDB(), m.TableName(), startTime, startTime)
	start := carbon.CreateFromTimestamp(startTime).ToDateString()
	end := carbon.CreateFromTimestamp(endTime).ToDateString()
	tx := dal.Use(dbs.QueryDB).StareAgeGender.
		WithContext(ctx).
		Where(m.Date.Between(start, end))
	if merchantID != "" {
		tx = tx.Where(m.MerchantID.In(strings.Split(merchantID, ",")...))
	}
	if robotName != "" {
		tx = tx.Where(m.RobotName.In(strings.Split(robotName, ",")...))
	}
	tx = tx.Session(&gorm.Session{})
	// 合计
	totalList, _ := tx.
		Group(m.AgeRange).
		Select(m.AgeRange,
			m.Male.Sum().As(m.Male.ColumnName().String()),
			m.Female.Sum().As(m.Female.ColumnName().String())).
		Find()
	for _, v := range totalList {
		switch v.AgeRange {
		case 1:
			summary.MaleAgeRange1 += v.Male
			summary.FemaleAgeRange1 += v.Female
		case 2:
			summary.MaleAgeRange2 += v.Male
			summary.FemaleAgeRange2 += v.Female
		case 3:
			summary.MaleAgeRange3 += v.Male
			summary.FemaleAgeRange3 += v.Female
		case 4:
			summary.MaleAgeRange4 += v.Male
			summary.FemaleAgeRange4 += v.Female
		case 5:
			summary.MaleAgeRange5 += v.Male
			summary.FemaleAgeRange5 += v.Female
		case 6:
			summary.MaleAgeRange6 += v.Male
			summary.FemaleAgeRange6 += v.Female
		}
		summary.Total += (v.Male + v.Female)
	}
	// 总条目 机器+日期 分组
	total, _ = tx.Group(m.RobotName, m.Date).Count()

	// 列表 机器+日期 分组
	RobotDateList, _, _ := tx.Group(m.RobotName, m.Date).
		Select(m.Date, m.RobotName, m.MerchantID, m.LocationID).
		Order(m.Date.Desc(), m.RobotName).
		FindByPage(Offset(page, limit)) // 机器+日期 分页
	res = []StareAgeGenderList{}
	if len(RobotDateList) == 0 {
		return
	}
	var RobotGroup []string
	var DateGroup []string
	for _, v := range RobotDateList {
		if !lo.Contains(RobotGroup, v.RobotName) {
			RobotGroup = append(RobotGroup, v.RobotName)
		}
		if !lo.Contains(DateGroup, v.Date) {
			DateGroup = append(DateGroup, v.Date)
		}
	}

	LocationMap := make(map[string]string)
	ProvinceMap := make(map[string]string)
	CityMap := make(map[string]string)
	DistrictMap := make(map[string]string)
	AreaNameMap := make(map[string]string)
	areaIDList := []string{}
	MerchantIDNameMap := make(map[string]string) // id => name
	{
		// GRPC获取设备区域信息
		deviceClient, clientErr := client.GetDeviceAdminClient()
		if clientErr != nil {
			err = clientErr
			return
		}
		deviceReq := &devicec.LocationListReq{}
		deviceRes, err1 := deviceClient.LocationList(context.Background(), deviceReq)
		if deviceRes != nil && deviceRes.Code == 0 && err1 == nil {
			for _, v := range deviceRes.List {
				LocationMap[v.Id] = v.Name
				ProvinceMap[v.Id] = v.ProvinceId
				CityMap[v.Id] = v.CityId
				DistrictMap[v.Id] = v.DistrictId
				areaIDList = append(areaIDList, v.ProvinceId)
				areaIDList = append(areaIDList, v.CityId)
				areaIDList = append(areaIDList, v.DistrictId)
			}
		}
		// GRPC获取省市区名称
		SystemAdminClient, clientErr := client.GetSystemAdminClient()
		if clientErr != nil {
			err = clientErr
			return
		}

		var areaReq system_admin.AreaLisRequest
		areaReq.AreaIDs = areaIDList
		areaRes, err1 := SystemAdminClient.AreaList(context.Background(), &areaReq)
		if err1 != nil {
			logx.Error(context.Background(), "areas error", logx.Any("res", areaRes), logx.Any("err", err1.Error()))
			return
		}
		for _, v := range areaRes.Areas {
			AreaNameMap[v.ID] = v.Name
		}

		// 获取商户信息
		var req system_admin.MerchantListReq
		merchantList, merchantErr := SystemAdminClient.MerchantList(context.Background(), &req)
		if merchantErr != nil {
			logx.Error(context.Background(), "grpc 获取商户信息错误", logx.String("err", merchantErr.Error()))
			return
		}
		if merchantList != nil && merchantList.List != nil {
			for _, v := range merchantList.List {
				MerchantIDNameMap[v.MerchantId] = v.MerchantName
			}
		}
	}
	// 当前分页下【机器+日期】以年龄分组的男女总和
	RobotDateAgeRangeList, _ := dal.Use(dbs.QueryDB).StareAgeGender.
		WithContext(ctx).
		Where(m.RobotName.In(RobotGroup...)).
		Where(m.Date.In(DateGroup...)).
		Group(m.RobotName, m.Date, m.AgeRange).
		Select(m.Date, m.RobotName, m.AgeRange,
			m.Male.Sum().As(m.Male.ColumnName().String()),
			m.Female.Sum().As(m.Female.ColumnName().String())).
		Find()
	var RobotDateAgeRangeMap = make(map[string]map[int64]*models.StareAgeGender) // Robot+date => agerange => StareAgeGender
	for _, v := range RobotDateAgeRangeList {
		var ageRange = make(map[int64]*models.StareAgeGender)
		if _, ok := RobotDateAgeRangeMap[v.RobotName+"_"+v.Date]; ok {
			ageRange = RobotDateAgeRangeMap[v.RobotName+"_"+v.Date]
		}
		ageRange[v.AgeRange] = v
		RobotDateAgeRangeMap[v.RobotName+"_"+v.Date] = ageRange
	}
	for _, v := range RobotDateList {
		var item StareAgeGenderList
		item.Date = v.Date
		item.RobotName = v.RobotName
		item.MerchantID = v.MerchantID
		if _, ok := MerchantIDNameMap[v.MerchantID]; ok {
			item.MerchantName = MerchantIDNameMap[v.MerchantID]
		}
		item.LocationID = v.LocationID
		if _, ok := LocationMap[v.LocationID]; ok {
			item.Location = LocationMap[v.LocationID]
			if province, ok := AreaNameMap[ProvinceMap[v.LocationID]]; ok {
				item.Province = province
			}
			if city, ok := AreaNameMap[CityMap[v.LocationID]]; ok {
				item.City = city
			}
			if district, ok := AreaNameMap[DistrictMap[v.LocationID]]; ok {
				item.District = district
			}
		}
		// 数据统计
		if ageRangeStares, ok := RobotDateAgeRangeMap[v.RobotName+"_"+v.Date]; ok {
			for ageRange, val := range ageRangeStares { // 年龄段数据
				switch ageRange {
				case 1:
					item.MaleAgeRange1 = val.Male
					item.FemaleAgeRange1 = val.Female
				case 2:
					item.MaleAgeRange2 = val.Male
					item.FemaleAgeRange2 = val.Female
				case 3:
					item.MaleAgeRange3 = val.Male
					item.FemaleAgeRange3 = val.Female
				case 4:
					item.MaleAgeRange4 = val.Male
					item.FemaleAgeRange4 = val.Female
				case 5:
					item.MaleAgeRange5 = val.Male
					item.FemaleAgeRange5 = val.Female
				case 6:
					item.MaleAgeRange6 = val.Male
					item.FemaleAgeRange6 = val.Female
				}
				item.Total += (val.Male + val.Female)
			}
		}
		res = append(res, item)
	}
	return
}
